package com.baijia.web.controller.v1;

import com.baijia.authentication.AccountType;
import com.baijia.authentication.handler.exception.AuthenticationException;
import com.baijia.authentication.handler.exception.UrlMessageException;
import com.baijia.authentication.principal.Credentials;
import com.baijia.authentication.principal.SimpleWebApplicationServiceImpl;
import com.baijia.common.enums.ResponseStatus;
import com.baijia.services.impl.RegisteredServiceImpl;
import com.baijia.ticket.TicketCreationException;
import com.baijia.ticket.TicketException;
import com.baijia.validation.Assertion;
import com.baijia.web.config.PassportConfig;
import com.baijia.web.controller.AbstractAuthorizeController;
import com.baijia.web.dto.response.BaseResponse;
import com.baijia.web.util.URLUtil;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("/v1")
public class AuthorizeController extends AbstractAuthorizeController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private PassportConfig passportConfig;

    @RequestMapping(value = "/doLogin.do", method = { RequestMethod.POST, RequestMethod.GET })
    @ResponseBody
    public void doLogin(HttpServletRequest request, HttpServletResponse response) throws NumberFormatException,
        IOException {

        // APPID 不能为空
        String appId = request.getParameter(APPID);
        if (StringUtils.isBlank(appId)) {
            logger.info("appid is null");
            response.sendRedirect(URLUtil.getLoginAddrWithError(servicesManager, appId,
                ResponseStatus.PARAM_NULL_APPID, passportConfig.getLoginUrl()));
            return;
        }

        // 生成credentials
        Credentials credentials = generateCredentials(request);
        if (credentials == null) {
            response.sendRedirect(URLUtil.getLoginAddrWithError(servicesManager, appId,
                ResponseStatus.ACCOUNT_NOT_SUPPORT, passportConfig.getLoginUrl()));
            return;
        } else if (StringUtils.isBlank(credentials.getUsername())) {
            // credentials = null;
            response.sendRedirect(URLUtil.getLoginAddrWithError(servicesManager, appId,
                ResponseStatus.ACCOUNT_NOT_SUPPORT, passportConfig.getLoginUrl()));
            return;
        }

        // 生成TGT，存储redis，生成ST，跳转orginalUrl
        SimpleWebApplicationServiceImpl service = null;
        try {
            service = SimpleWebApplicationServiceImpl.createServiceFrom(request, httpClient);
            logger.info("service:" + service);
            if (!service.validDomain(servicesManager.findServiceBy(service))) {
                logger.error("valid domain error!original url :{},domains:{}", service.getOriginalUrl(),
                    servicesManager.findServiceBy(service).getAllowDomains() + "|service:" + service);
                // FIXME 应跳转无权限页面，当前为临时页面 
                response.sendRedirect(URLUtil.getNoauthUrl(request));
                return;
            }
            String TGTID = cookieRetrievingCookieGenerator.retrieveCookieValue(request);
            String ST = null;
            if (StringUtils.isBlank(TGTID)) {
                // create
                TGTID = centralAuthenticationService.createTicketGrantingTicket(credentials);
                ST = centralAuthenticationService.grantServiceTicket(TGTID, service);
                cookieRetrievingCookieGenerator.addCookie(request, response, TGTID);
            } else {
                // from cookie
                ST = centralAuthenticationService.grantServiceTicket(TGTID, service, credentials);
            }
            String validUrl = URLUtil.getValidSTUrl(service, ST, passportConfig.getLoginUrl());
            logger.info("validUrl:" + validUrl);
            response.sendRedirect(validUrl);
        } catch (UrlMessageException e) {
            clearCookieAndSession(request, response);
            logger.info("login fail! credentials:{},msg:{}", credentials, e.getMessage());
            response.sendRedirect(URLUtil.getLoginAddrWithMsg(servicesManager, appId, e, service.getOriginalUrl()));
        } catch (AuthenticationException e) {
            clearCookieAndSession(request, response);
            logger.info("createTicketGrantingTicket error!credentials:" + credentials, e);
            response.sendRedirect("logout.do?service=" + service.getOriginalUrl());
        } catch (TicketCreationException e2) {
            clearCookieAndSession(request, response);
            logger.error("TicketCreationException!" + credentials, e2);
        } catch (Exception e) {
            clearCookieAndSession(request, response);
            logger.error("System error!credentials:" + credentials, e);
            response.sendRedirect("logout.do?service=" + service == null ? "" : service.getOriginalUrl());
        }
        return;
    }

    @RequestMapping({ "/validateTicket.do", "/validateServiceTicket.do" })
    @ResponseBody
    public BaseResponse validateServiceTicket(HttpServletRequest request, HttpServletResponse response)
        throws NumberFormatException, IOException {
        SimpleWebApplicationServiceImpl service = SimpleWebApplicationServiceImpl.createServiceFrom(request);
        String serviceTicketId = service != null ? service.getArtifactId() : null;

        BaseResponse res = new BaseResponse();
        // APPID 不能为空
        String appId = request.getParameter(APPID);
        if (StringUtils.isBlank(appId)) {
            res.setMsg(ResponseStatus.PARAM_NULL_APPID.getMsg());
            res.setStatus(ResponseStatus.PARAM_NULL_APPID.getCode());
            return res;
        }

        if (service == null || serviceTicketId == null) {
            logger.debug(String.format("Could not process request; Service: %s, Service Ticket Id: %s", service,
                serviceTicketId));
            res.setMsg(ResponseStatus.NO_TICKET.getMsg());
            res.setStatus(ResponseStatus.NO_TICKET.getCode());
            return res;
        }

        try {
            Assertion assertion = this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service);
            res.setAssertion(assertion);
            res.setMsg(ResponseStatus.OK.getMsg());
            res.setStatus(ResponseStatus.OK.getCode());
            return res;
        } catch (AuthenticationException e) {
            clearCookieAndSession(request, response);
            logger.error("validateServiceTicket biz error!service:" + service, e);
            res.setMsg(ResponseStatus.AUTH_FAIL.getMsg());
            res.setStatus(ResponseStatus.AUTH_FAIL.getCode());
        } catch (Exception e) {
            clearCookieAndSession(request, response);
            logger.error("validateServiceTicket sys error!service:" + service, e);
            res.setMsg(ResponseStatus.SYSTEM_ERROR.getMsg());
            res.setStatus(ResponseStatus.SYSTEM_ERROR.getCode());
        }
        return res;
    }

    @RequestMapping("/gainSt.do")
    public void gainSt(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String appId = request.getParameter(Credentials.APPID);
        AccountType accountType = AccountType.getAccountTypeByCode(Integer.valueOf(appId));
        String tgtId;
        switch (accountType) {
            case USER:
            case ORG_TEACHER:
                tgtId = cookieRetrievingCookieGenerator.retrieveUserCookieValue(request);
                break;
            default:
                tgtId = cookieRetrievingCookieGenerator.retrieveCookieValue(request);
                break;
        }
        // String targetService = request.getParameter("targetService");
        String st;
        SimpleWebApplicationServiceImpl targetService;
        targetService = SimpleWebApplicationServiceImpl.createServiceFrom(request, httpClient);
        logger.info("targetService:" + targetService);
        if (!targetService.validDomain(servicesManager.findServiceBy(targetService))) {
            logger.error("valid domain error!original url :{},domains:{}", targetService.getOriginalUrl(),
                servicesManager.findServiceBy(targetService).getAllowDomains() + "|targetService:" + targetService);
            // FIXME 应跳转无权限页面，当前为临时页面 
            response.sendRedirect(URLUtil.getNoauthUrl(request));
            return;
        }
        logger.info("gainSt: tgtId: {}", tgtId);
        if (!StringUtils.isBlank(tgtId)) {
            try {
                st = centralAuthenticationService.grantServiceTicket(tgtId, targetService, null);
                String validUrl = URLUtil.getValidSTUrl(targetService, st, passportConfig.getLoginUrl());
                logger.info("validUrl:" + validUrl);
                response.sendRedirect(validUrl);
            } catch (AuthenticationException e) {
                e.printStackTrace();
                logger.info("createTicketGrantingTicket error!targetService:" + targetService, e);
                response.sendRedirect("logout.do?service=" + targetService.getOriginalUrl() + "&appId=" + appId);
            } catch (TicketException e) {
                logger.error("Ticket exception!", e);
                response.sendRedirect("logout.do?service=" + targetService.getOriginalUrl() + "&appId=" + appId);
            }
        }
    }

    private void clearCookieAndSession(HttpServletRequest request, HttpServletResponse response) {
        String appId = request.getParameter(Credentials.APPID);
        AccountType accountType = AccountType.getAccountTypeByCode(Integer.valueOf(appId));
        String ticketGrantingTicketId;
        switch (accountType) {
            case USER:
            case ORG_TEACHER:
                ticketGrantingTicketId = cookieRetrievingCookieGenerator.retrieveUserCookieValue(request);
                break;
            default:
                ticketGrantingTicketId = cookieRetrievingCookieGenerator.retrieveCookieValue(request);
                break;
        }
        logger.info("logout TGT:{}", ticketGrantingTicketId);
        handleLogout(ticketGrantingTicketId, request, response);
    }

    @RequestMapping("/logout.do")
    public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {
        clearCookieAndSession(request, response);
        // service代表请求的url
        String service = request.getParameter("service");
        String targetService = request.getParameter("targetService");
        if (StringUtils.isNotBlank(targetService)) {
            logger.info("targetService:{}", targetService);
            response.sendRedirect(targetService);
        } else if (StringUtils.isNotBlank(service)) {
            logger.info("service:{}", service);
            SimpleWebApplicationServiceImpl webService = new SimpleWebApplicationServiceImpl(service);
            RegisteredServiceImpl rService = (RegisteredServiceImpl) this.servicesManager.findServiceBy(webService);
            response.sendRedirect(rService.getLoginAddr());
        } else {
            // FIXME 正式的noauth待做
            response.sendRedirect(URLUtil.getNoauthUrl(request));
        }
    }
}
